
//  Software Guide : BeginLatex
//
//  This example illustrates how to read a single DICOM slice and write it
//  back as another DICOM slice. In the process an intensity rescaling is also
//  applied.
//
//  In order to read and write the slice we use the \doxygen{GDCMImageIO}
//  class which encapsulates a connection to the underlying GDCM library. In
//  this way we gain access from ITK to the DICOM functionalities offered by
//  GDCM. The GDCMImageIO object is connected as the ImageIO object to be used
//  by the \doxygen{ImageFileWriter}.
//
//  We should first include the following header files.
//
//  Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkImageLinearIteratorWithIndex.h"
#include "itkGDCMImageIO.h"
// Software Guide : EndCodeSnippet

#include <list>
#include <fstream>

int
main(int argc, char * argv[])
{
    // Verify the number of parameters in the command line
    if (argc < 5)
    {
        std::cerr << "Usage: " << std::endl;
        std::cerr << argv[0] << " DicomImage OutputDicomImage ";
        std::cerr << " OutputImage RescaleDicomImage\n";
        return EXIT_FAILURE;
    }

    using InputPixelType = signed short;
    constexpr unsigned int InputDimension = 2;

    using InputImageType = itk::Image<InputPixelType, InputDimension>;

    using ReaderType = itk::ImageFileReader<InputImageType>;

    ReaderType::Pointer reader = ReaderType::New();
    reader->SetFileName(argv[1]);

    using ImageIOType = itk::GDCMImageIO;
    ImageIOType::Pointer gdcmImageIO = ImageIOType::New();
    reader->SetImageIO(gdcmImageIO);

    try
    {
        reader->Update();
    }
    catch (const itk::ExceptionObject & e)
    {
        std::cerr << "exception in file reader " << std::endl;
        std::cerr << e << std::endl;
        return EXIT_FAILURE;
    }

    using Writer1Type = itk::ImageFileWriter<InputImageType>;

    Writer1Type::Pointer writer1 = Writer1Type::New();

    writer1->SetFileName(argv[2]);
    writer1->SetInput(reader->GetOutput());

    //  We need to explicitly set the proper image IO (GDCMImageIO) to the
    //  writer filter since the input DICOM dictionary is being passed along the
    //  writing process. The dictionary contains all necessary information that
    //  a valid DICOM file should contain, like Patient Name, Patient ID,
    //  Institution Name, etc.

    writer1->SetImageIO(gdcmImageIO);

    try
    {
        writer1->Update();
    }
    catch (const itk::ExceptionObject & e)
    {
        std::cerr << "exception in file writer " << std::endl;
        std::cerr << e << std::endl;
        return EXIT_FAILURE;
    }

    //  We will now rescale the image using the RescaleIntensityImageFilter. For
    //  this purpose we use a better suited pixel type: \code{unsigned char}
    //  instead of \code{signed short}.  The minimum and maximum values of the
    //  output image are explicitly defined in the rescaling filter.
    //
    using WritePixelType = unsigned short;

    using WriteImageType = itk::Image<WritePixelType, 2>;

    using RescaleFilterType =
    itk::RescaleIntensityImageFilter<InputImageType, WriteImageType>;

    RescaleFilterType::Pointer rescaler = RescaleFilterType::New();

    rescaler->SetOutputMinimum(0);
    rescaler->SetOutputMaximum(65535);


    // We create a second writer object that will save the rescaled image into a
    // new file, which is not in DICOM format. This is done only for the sake of
    // verifying the image against the one that will be saved in DICOM format
    // later in this example.
    //
    using Writer2Type = itk::ImageFileWriter<WriteImageType>;

    Writer2Type::Pointer writer2 = Writer2Type::New();

    writer2->SetFileName(argv[3]);

    rescaler->SetInput(reader->GetOutput());
    writer2->SetInput(rescaler->GetOutput());

    try
    {
        writer2->Update();
    }
    catch (const itk::ExceptionObject & e)
    {
        std::cerr << "exception in file writer " << std::endl;
        std::cerr << e << std::endl;
        return EXIT_FAILURE;
    }


    // We proceed now to save the same rescaled image into a file in DICOM
    // format. For this purpose we just need to set up a
    // \doxygen{ImageFileWriter} and pass to it the rescaled image as input.
    //
    using Writer3Type = itk::ImageFileWriter<WriteImageType>;

    Writer3Type::Pointer writer3 = Writer3Type::New();

    writer3->SetFileName(argv[4]);
    writer3->SetInput(rescaler->GetOutput());

    // We now need to explicitly set the proper image IO (GDCMImageIO), but also
    // we must tell the ImageFileWriter to not use the MetaDataDictionary from
    // the input but from the GDCMImageIO since this is the one that contains
    // the DICOM specific information
    //
    // The GDCMImageIO object will automatically detect the pixel type, in this
    // case \code{unsigned char} and it will update the DICOM header information
    // accordingly.
    // \index{itk::ImageFileWriter!UseInputMetaDataDictionaryOff()}
    //
    writer3->UseInputMetaDataDictionaryOff();
    writer3->SetImageIO(gdcmImageIO);

    // Finally we trigger the execution of the DICOM writer by invoking the
    // Update() method from inside a try/catch block.
    try
    {
        writer3->Update();
    }
    catch (const itk::ExceptionObject & e)
    {
        std::cerr << "Exception in file writer " << std::endl;
        std::cerr << e << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
